import { toStr } from "../../utils/toString.js";
import ValuePair from "./ValuePair.js";

export default class Dictionary {
  constructor() {
    this.table = {};
  }

  /**
   * 查询字典中是否存在该key
   * @param {string} key 键
   * @returns {boolean}
   */
  hasKey(key) {
    return this.table[toStr(key)] != null;
  }

  /**
   * 向字典中添加或更新一个[键, 值]对
   * @param {string} key 键
   * @param {*} value 内容
   * @returns {Dictionary}
   */
  set(key, value) {
    if (key != null && value != null) {
      this.table[toStr(key)] = new ValuePair(key, value);
    }
    return this;
  }

  /**
   * 从字典中移除一个值
   * @param {string} key
   * @returns {boolean}
   */
  remove(key) {
    if (this.hasKey(key)) {
      delete this.table[toStr(key)];
      return true;
    }
    return false;
  }

  /**
   * 从字典中检索一个值
   * @param {string} key
   * @returns {*|null}
   */
  get(key) {
    const result = this.table[toStr(key)];
    return result ? result.value : null;
  }

  /**
   * 返回所有[键, 值]对组成的数组
   * @returns {*[]}
   */
  keyValues() {
    return Object.values(this.table);
  }

  /**
   * 返回所有键组成的数组
   * @returns {*[]}
   */
  keys() {
    return this.keyValues().map((valuePair) => valuePair.key);
  }

  /**
   * 返回所有值组成的数组
   * @returns {*[]}
   */
  values() {
    return this.keyValues().map((valuePair) => valuePair.value);
  }

  /**
   * 迭代字典
   * @param {function} callback 回调函数支持两个参数：key, value
   */
  forEach(callback) {
    const valuePairs = this.keyValues();
    for (let i = 0; i < valuePairs.length; i++) {
      const result = callback(valuePairs[i].key, valuePairs[i].value);
      // 当回调函数callback返回false时，跳出循环，停止迭代
      if (result === false) {
        break;
      }
    }
  }

  /**
   * 获取字典的大小
   * @returns {number}
   */
  size() {
    return Object.keys(this.table).length;
  }

  /**
   * 查询字典是否为空
   * @returns {boolean}
   */
  isEmpty() {
    return this.size() === 0;
  }

  /**
   * 清空字典
   */
  clear() {
    this.table = {};
  }

  /**
   * 返回字典的字符串形式
   * @param {function} callback 回调函数支持两个参数：key, value
   * @returns {string}
   */
  toString(callback) {
    return this.keyValues()
      .map((valuePair) => valuePair.toString(callback))
      .toString();
  }
}
